package com.poizon.infrastructure.idem.core.domain.entity;

import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.poizon.infrastructure.idem.core.repo.IIdemDbStoreRepo;
import com.poizon.infrastructure.idem.core.domain.value.exception.IdempotentDbStoreException;
import com.poizon.infrastructure.idem.core.domain.value.exception.IdempotentException;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;

import java.io.Serializable;
import java.util.Date;
import java.util.concurrent.TimeUnit;

import static com.poizon.infrastructure.idem.core.domain.value.exception.IdempotentErrorEnum.UPDATE_IDEM_RESP_ERROR;

/**
 * 领域模型/幂等
 */
@Slf4j
@Data
public class IdemEntity implements Serializable {
    // 故障转移最大重试次数
    public static IIdemDbStoreRepo iIdemDbStoreRepo;

    /**
     * 幂等键
     */
    private String reqId;

    /**
     * 幂等请求响应
     */
    private Object resp;

    /**
     * 幂等请求来源类型
     */
    private String reqType;

    /**
     * 幂等数据创建时间
     */
    private Date createTime;

    /**
     * 状态字段
     * 用来判断返回结果是否已经写入(主要用来兼容void)
     * @true resp已经更新到数据库
     * @false resp还没有更新到数据库
     */
    private Boolean status = false;

    public static class Builder {
        /**
         * 构建/幂等插入对象
         */
        public static IdemEntity buildCreateIdemDto(String idemKey, String reqType) {
                log.debug(">开始构建幂等空对象,buildCreateIdemDto() called with parameters => 【idemKey = {}】, 【reqType = {}】", idemKey, reqType);

            IdemEntity idemEntity = new IdemEntity();
            idemEntity.setReqId(idemKey);
            idemEntity.setResp(null);
            idemEntity.setReqType(reqType);
            idemEntity.setCreateTime(new Date());
            idemEntity.setStatus(false);

            if (log.isDebugEnabled()) {
                log.debug(">成功构建幂等空对象={}", JSON.toJSONString(idemEntity));
            }

            return idemEntity;
        }
    }

    public static class Get {
        /**
         * 场景1= 单独线程获取
         * 场景2= 多线程同时获取
         * 场景3= 单线程获取 同时其他线程在操作其他方法
         */
        public static IdemEntity getIdemDto(String idemKey) {
            try {
                    log.debug(">尝试根据idemKey={},获取幂等数据", idemKey);
                IdemEntity idemEntity = iIdemDbStoreRepo.getIdemDto(idemKey);
                if (idemEntity == null) {
                    log.debug(">无法获取idemKey={}的幂等数据", idemKey);
                    return null;
                }
                if (log.isDebugEnabled()) {
                    log.debug(">幂等key={},已执行幂等操作,不允许重复执行,resp={}", idemKey, JSON.toJSONString(idemEntity));
                }
                return idemEntity;
            } catch (Exception e) {
                log.error(StrUtil.format(">根据idemKey={},获取幂等数据异常", idemKey), e);
                throw new IdempotentDbStoreException(e);
            }
        }
    }

    /**
     * 设置幂等键
     */
    public Boolean setIdemKey(String idemKey, IdemEntity emptyRespIdemEntity, long expireTime, TimeUnit timeUnit) {
        try {
            Boolean success = iIdemDbStoreRepo.setIdemKey(idemKey, emptyRespIdemEntity, expireTime, timeUnit);
            if (!success) {
                log.debug(">幂等key={},已经存在,设置失败", idemKey);
                return false;
            }
            log.debug(">幂等key={},空对象设置成功", idemKey);
            return true;
        } catch (Exception e) {
            log.error(StrUtil.format(">根据idemKey={},设置幂等数据异常", idemKey), e);
            throw new IdempotentDbStoreException(e);
        }
    }

    /**
     * 更新幂等响应数据
     */
    public void updateIdemResp(Object resp, long expireTime, TimeUnit timeUnit, String idemKey, IdemEntity emptyRespIdemEntity) {
        emptyRespIdemEntity.setResp(resp);
        emptyRespIdemEntity.setStatus(true);

        try {
            // 针对更新幂等响应数据重试
            if (log.isDebugEnabled()) {
                log.debug(">更新幂等key={},响应={},ing", idemKey, JSON.toJSONString(emptyRespIdemEntity));
            }

            try {
                iIdemDbStoreRepo.updateIdemResp(idemKey, emptyRespIdemEntity, expireTime, timeUnit);
            } catch (Exception e) {
                log.error(StrUtil.format(">更新幂等响应失败,请人工检查,IdemEntity={}", JSON.toJSONString(emptyRespIdemEntity)), e);
                throw new IdempotentException(UPDATE_IDEM_RESP_ERROR);
            }
        } catch (IdempotentException e) {
            log.error(StrUtil.format(">根据idemKey={},更新幂等响应数据异常", idemKey), e);
            throw new IdempotentDbStoreException(e);
        }
    }

    /**
     * 幂等key/回退
     */
    public void delete(String idemKey) {
            log.debug(">删除幂等key={},ing", idemKey);

        // 删除失败,重试几次,到达故障转移阈值,然后直接故障转移
        try {
            iIdemDbStoreRepo.delete(idemKey);
        } catch (Exception e) {
            log.error(StrUtil.format(">根据idemKey={},删除幂等数据异常", idemKey), e);
            throw new IdempotentDbStoreException(e);
        }

    }
}
